package com.yixing.tech.system.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.yixing.tech.common.base.*;
import com.yixing.tech.common.enums.ErrCode;
import com.yixing.tech.common.enums.Status;
import com.yixing.tech.common.enums.UserType;
import com.yixing.tech.common.enums.YesOrNo;
import com.yixing.tech.system.entity.WxLoginResponse;
import com.yixing.tech.system.po.Department;
import com.yixing.tech.system.po.Role;
import com.yixing.tech.system.po.Tenant;
import com.yixing.tech.system.po.User;
import com.yixing.tech.system.service.IMenuService;
import com.yixing.tech.system.service.IUserService;
import com.yixing.tech.system.utils.PortalConfig;
import com.yixing.tech.common.utils.*;
import com.yixing.tech.common.vo.s3.TenantVO;
import com.yixing.tech.common.vo.s3.UserVO;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.LimitOperation;
import org.springframework.data.mongodb.core.aggregation.SkipOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author yixing tech
 * at 2021/12/21
 * describe：
 */
@Service
public class UserServiceImpl implements IUserService {

    @Resource
    BaseMongoDao baseMongoDao;
    @Resource
    MongoTemplate mongoTemplate;
    @Resource
    IMenuService menuService;


    @Override
    public void add(UserVO userVO) throws BusinessException {
        String loginName = userVO.getLoginName();
        if (!CommonUtil.checkPhone(loginName)) {
            throw new BusinessException(ErrCode.PARAMETER_ERROR);
        }
        UserVO condition = new UserVO();
        condition.setLoginName(userVO.getLoginName());
        condition.setTenantId(userVO.getTenantId());
        if (baseMongoDao.exists(condition, User.class)) {
            throw new BusinessException(ErrCode.USER_EXISTED);
        }

        String md5Password = CommonUtil.buildDefaultPassword(loginName);
        User user = DataUtil.o2o(userVO, User.class);
        user.setId(null);
        user.setIsSuper(false);
        user.setStatus(Status.def.ordinal());
        user.setPassword(md5Password);
        user.setPhone(loginName);
        user.setType(UserType.平台用户);
        baseMongoDao.save(user);
    }

    @Override
    public void batchAdd(List<UserVO> list) throws BusinessException {

    }

    @Override
    public UserVO queryById(String id) {
        User user = baseMongoDao.get(id, User.class);
        user.setPassword(null);
        return DataUtil.o2o(user, UserVO.class);
    }

    @Override
    public List<UserVO> queryByIds(List<String> ids) {
        return null;
    }

    @Override
    public UserVO queryByCondition(UserVO userVO) throws BusinessException {
        return null;
    }

    @Override
    public void update(UserVO userVO) throws BusinessException {
        User user = DataUtil.o2o(userVO, User.class);
        user.setPassword(null);
        user.setTenantId(null);
        user.setLoginName(null);
        user.setIsSuper(null);
        user.setStatus(null);
        user.setCreateTime(null);
        user.setUpdateTime(new Date());
        Role selfRole = user.getSelfRole();
        if (selfRole != null) {
            selfRole.setId("1");
            List<String> menuIds = selfRole.getMenuIds();
            if (!CommonUtil.listIsBlank(menuIds)) {
                menuIds = menuService.fillParentId(menuIds);
            }
            List<String> applicationIds = selfRole.getApplicationIds();
            Map actions = selfRole.getActions();
            Map dataConfigs = selfRole.getDataConfigs();
            List<String> processIds = selfRole.getProcessIds();
            User oldUser = baseMongoDao.get(user.getId(), User.class);
            Role oldRole = oldUser.getSelfRole();
            if (oldRole != null) {
                user.setSelfRole(oldRole);
                if (menuIds != null) oldRole.setMenuIds(menuIds);
                if (applicationIds != null) oldRole.setApplicationIds(applicationIds);
                if (actions != null) oldRole.setActions(actions);
                if (dataConfigs != null) oldRole.setDataConfigs(dataConfigs);
                if (processIds != null) oldRole.setProcessIds(processIds);
            }

        }
        baseMongoDao.update(user);
    }

    @Override
    public Page<UserVO> listPage(PageRow<ConditionsVO> pageRow) {
        List<AggregationOperation> operations = new ArrayList<>();
        List<Criteria> criteriaList = baseMongoDao.condition2CriteriaList(pageRow.getCondition());
        Criteria criteria = new Criteria().andOperator(criteriaList);
        operations.add(Aggregation.match(criteria));

        SkipOperation skipOperation = Aggregation.skip(Long.valueOf(pageRow.getPageNo() * pageRow.getPageSize()));
        operations.add(skipOperation);

        LimitOperation limitOperation = Aggregation.limit(pageRow.getPageSize());
        operations.add(limitOperation);

        operations.add(Aggregation.lookup("role", "role._id", "_id", "role"));
        operations.add(Aggregation.unwind("role", true));

        operations.add(Aggregation.lookup("department", "department._id", "_id", "department"));
        operations.add(Aggregation.unwind("department", true));
        List<UserVO> l = baseMongoDao.aggregate(operations, "user", UserVO.class).getMappedResults();
        long count = baseMongoDao.count(criteria, User.class);
        return new Page<>(l, pageRow.getPageSize(), pageRow.getPageNo() + 1, count);
    }

    @Override
    public UserVO updateWxUser(String phoneStr, String wxName, String avatarUrl, String userId) throws BusinessException {
        User user = baseMongoDao.get(userId, User.class);
        String phone = phoneStr;
        Criteria andCriteria = new Criteria();
        Criteria orCriteria = new Criteria();
        orCriteria.orOperator(Criteria.where("loginName").is(phone), Criteria.where("phone").is(phone));
        andCriteria.andOperator(Criteria.where("tenantId").is(user.getTenantId()).and("isDelete").ne(YesOrNo.YES.ordinal()), orCriteria);
        User platformUser = mongoTemplate.findOne(Query.query(andCriteria), User.class);
        if (platformUser != null) {
            if (platformUser.getId() == userId) return DataUtil.o2o(user, UserVO.class);
            platformUser.setLoginName(user.getLoginName());
            platformUser.setType(UserType.微信用户);
            platformUser.setHeadPicUrl(avatarUrl);
            platformUser.setPhone(phone);
            baseMongoDao.update(platformUser);
            user = platformUser;
            baseMongoDao.delete(userId, User.class);
        } else {
            user.setName(wxName);
            user.setPhone(phone);
            user.setHeadPicUrl(avatarUrl);
            baseMongoDao.update(user);
        }

        return DataUtil.o2o(user, UserVO.class);

    }

    @Override
    public void bindWx(String jsCode, String userId, String tenantId) throws BusinessException {
        RestTemplate restTemplate = new RestTemplate();
        TenantVO cond = new TenantVO();
        cond.setId(tenantId);
        cond.setStatus(Status.def.ordinal());
        Tenant tenant = baseMongoDao.queryOne(Tenant.class, cond);
        if (tenant == null) {
            throw new BusinessException(ErrCode.TENANT_FREEZE);
        }
        String wpStr = restTemplate.getForObject(PortalConfig.wxLoginHttp, String.class, tenant.getAppletAppId(), tenant.getAppletSecret(), jsCode);
        WxLoginResponse wp = JSONObject.parseObject(wpStr, WxLoginResponse.class);
        if (wp.isOk()) {
            User user = new User();
            user.setId(userId);
            String unionid = wp.getUnionid();
            user.setUnionId(unionid);
            if (StringUtils.hasText(unionid)) {
                user.setType(UserType.微信用户);
                baseMongoDao.update(user);
            }
        }
    }

    @Override
    public void unBindWx(String userId) throws BusinessException {
        User user = new User();
        user.setId(userId);
        user.setType(UserType.平台用户);
        baseMongoDao.update(user);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void register(String phone, String pw, String verificationCode) throws BusinessException {
        if (!CommonUtil.checkPhone(phone)) {
            throw new BusinessException(ErrCode.PARAMETER_ERROR);
        }
        // 创建个人组织
        TenantVO condition = new TenantVO();
        condition.setName(phone);
        if (baseMongoDao.exists(condition, Tenant.class)) {
            throw new BusinessException(ErrCode.USER_EXISTED);
        }
        Tenant tenant = DataUtil.o2o(condition, Tenant.class);
        tenant.setStatus(Status.def.ordinal());
        tenant.setClazz("personal");
        String tenantId = baseMongoDao.save(tenant).getId();
        //创建管理员角色,绑定门户应用
        Role role = new Role();
        role.setName("个人管理员");
        role = baseMongoDao.save(role);
        //创建用户，绑定角色
        User user = new User();
        user.setLoginName(phone);
        user.setIsSuper(false);
        user.setIsAdmin(true);
        user.setStatus(Status.def.ordinal());

        // TODO 改 为 Spring 的方式
        String password = DESUtil.encrypt(pw);
        String md5Password = MD5Util.MD5(password);

        user.setPassword(md5Password);
        user.setPhone(phone);
        user.setType(UserType.平台用户);
        user.setTenantId(tenantId);
        user.setRole(role);
        baseMongoDao.save(user);
    }

    @Override
    public List<UserVO> list(UserVO condition) {
        List<AggregationOperation> operations = new ArrayList<>();
        Criteria criteria = baseMongoDao.condition2Criteria(condition);
        operations.add(Aggregation.match(criteria));
        operations.add(Aggregation.lookup("role", "role._id", "_id", "role"));
        operations.add(Aggregation.unwind("role", true));

        operations.add(Aggregation.lookup("department", "department._id", "_id", "department"));
        operations.add(Aggregation.unwind("department", true));

        List<UserVO> l = baseMongoDao.aggregate(operations, "user", UserVO.class).getMappedResults();
        return l;
    }

    @Override
    public void deleteById(String id) throws BusinessException {
        User user = new User();
        user.setId(id);
        user.setIsDelete(YesOrNo.YES.ordinal());
        baseMongoDao.update(user);
    }

    @Override
    public void delete(List<String> ids) throws BusinessException {
//        baseMongoDao.delete(ids, User.class);
    }

    @Override
    public void modifyPW(UserVO vo) throws BusinessException {
        String oldPassword = vo.getOldPassword();
        String password = vo.getPassword();

        if (!StringUtils.hasText(oldPassword) || !StringUtils.hasText(password)) {
            throw new BusinessException(ErrCode.PARAMETER_ERROR);
        }
        String oldMD5Password = MD5Util.MD5(oldPassword);


        UserVO cond = new UserVO();
        cond.setId(vo.getId());
        User user = baseMongoDao.queryOne(User.class, cond);
        if (!oldMD5Password.equals(user.getPassword())) {
            throw new BusinessException(ErrCode.USER_PASSWORD_ERROR);
        }
        String md5Password = MD5Util.MD5(password);
        user.setPassword(md5Password);
        baseMongoDao.update(user);
    }

    @Override
    public void resetPW(UserVO vo) throws BusinessException {
        User user = baseMongoDao.get(vo.getId(), User.class);
        String loginName = user.getLoginName();
        String md5Password = CommonUtil.buildDefaultPassword(loginName);
        user.setPassword(md5Password);
        baseMongoDao.update(user);
    }

    @Override
    public void freezeOrUnFreeze(String userId, boolean isFreeze) throws BusinessException {
        User user = new User();
        user.setId(userId);
        user.setStatus(isFreeze ? Status.freeze.ordinal() : Status.def.ordinal());
        baseMongoDao.update(user);
    }

    @Override
    public void importUsers(MultipartFile file, UserVO loginUser) throws Exception {
        List<Map<String, Object>> list = ExcelUtil.excelToList(file);
        List<User> users = new ArrayList<>();
        for (Map<String, Object> m : list) {
            User user = new User();
            user.setCode(m.getOrDefault("工号", "").toString());
            user.setName(m.getOrDefault("名称", "").toString());
            String loginName = m.getOrDefault("手机号", "").toString();
            UserVO condition = new UserVO();
            condition.setLoginName(loginName);
            user.setPhone(loginName);
            if (baseMongoDao.exists(condition, User.class)) {
                continue;
            }
            user.setLoginName(loginName);
            user.setEmail(m.getOrDefault("邮箱", "").toString());
            user.setDesc(m.getOrDefault("描述", "").toString());
            String departmentId = m.getOrDefault("部门", "").toString();
            if (StringUtils.hasText(departmentId)) {
                Department depart = new Department();
                depart.setId(departmentId);
                user.setDepartment(depart);
            }
            String md5Password = CommonUtil.buildDefaultPassword(loginName);
            user.setPassword(md5Password);
            user.setCreateId(loginUser.getId());
            user.setTenantId(loginUser.getTenantId());
            user.setCreateName(loginUser.getName());
            user.setType(UserType.平台用户);
            users.add(user);
        }
        baseMongoDao.batchSave(users, User.class);
    }

    @Override
    public void getImportTemplate(HttpServletResponse response) throws BusinessException {
        List<String> titles = new ArrayList<>();
        titles.add("工号");
        titles.add("名称");
        titles.add("手机号");
        titles.add("邮箱");
        titles.add("部门");
        titles.add("描述");
        ExcelUtil.buildExcelTemplate(response, "user.xls", titles);
    }

    @Override
    public Page<UserVO> list(PageRow<UserVO> pageRow) {
        return null;
    }
}
